﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using EuroManager.Domain.Simulator.Engine;

namespace EuroManager.Domain.Simulator.Helpers
{
    public static class Physics
    {
        public static Vector2D Location(Vector2D location, Vector2D velocity, float time)
        {
            Contract.Requires(time.IsRational() && time > 0);

            return location + velocity * time;
        }

        public static Vector2D Location(Vector2D location, Vector2D velocity, float acceleration, float time)
        {
            Contract.Requires(acceleration.IsRational());
            Contract.Requires(time.IsRational() && time >= 0);

            return location + velocity.Normalized() * Distance(velocity.Length, acceleration, time);
        }

        public static float Time(float speed, float distance)
        {
            Contract.Requires(speed.IsRational() && speed > 0);
            Contract.Requires(distance.IsRational() && distance >= 0);

            return distance / speed;
        }

        public static float Time(float startSpeed, float finalSpeed, float acceleration)
        {
            Contract.Requires(startSpeed.IsRational() && startSpeed >= 0);
            Contract.Requires(finalSpeed.IsRational() && finalSpeed >= 0);
            Contract.Requires(acceleration.IsRational());
            Contract.Requires(finalSpeed == startSpeed || Maths.Sign(finalSpeed - startSpeed) == Maths.Sign(acceleration));

            return acceleration == 0 ? 0 : (finalSpeed - startSpeed) / acceleration;
        }

        public static Vector2D Velocity(Vector2D velocity, Vector2D acceleration, float time)
        {
            Contract.Requires(time.IsRational() && time > 0);

            return velocity + acceleration * time;
        }

        public static Vector2D Velocity(Vector2D velocity, Vector2D acceleration, float time, float maxSpeed)
        {
            Contract.Requires(maxSpeed.IsRational() && maxSpeed >= 0);

            return Velocity(velocity, acceleration, time).Truncated(maxSpeed);
        }

        public static Vector2D Velocity(Vector2D velocity, float deceleration, float time)
        {
            Contract.Requires(deceleration.IsRational() && deceleration >= 0);
            Contract.Requires(time.IsRational() && time > 0);

            return velocity - velocity.Normalized() * deceleration * time;
        }

        public static float StartSpeed(float finalSpeed, float distance, float deceleration)
        {
            Contract.Requires(finalSpeed.IsRational() && finalSpeed >= 0);
            Contract.Requires(distance.IsRational() && distance > 0);
            Contract.Requires(deceleration.IsRational() && deceleration >= 0);

            return Maths.Sqrt(2 * deceleration * distance + finalSpeed * finalSpeed);
        }

        public static float Distance(float speed, float deceleration)
        {
            Contract.Requires(speed.IsRational() && speed >= 0);
            Contract.Requires(deceleration.IsRational() && deceleration > 0);

            float time = speed / deceleration;
            return Distance(speed, -deceleration, time);
        }

        public static float Distance(float speed, float acceleration, float time)
        {
            Contract.Requires(speed.IsRational() && speed >= 0);
            Contract.Requires(acceleration.IsRational());
            Contract.Requires(time.IsRational() && time >= 0);

            return speed * time + acceleration * time * time / 2;
        }
    }
}
